package bma.common.langutil.cron;

import java.util.StringTokenizer;

/**
 * Crontab
 * 
 * @author guanzhong
 * 
 */
public class CrontabParser {

	/**
	 * format: min hour dayOfMonth month dayOfWeek
	 * 
	 * @param entry
	 *            the line to parse
	 * @throws CrontabEntryException
	 *             Error parsing the string
	 */
	public static CrontabSchedule marshall(String entry) {

		CrontabSchedule ceb = new CrontabSchedule();
		ceb.setEntry(entry);

		StringTokenizer tokenizer = new StringTokenizer(entry);

		int numTokens = tokenizer.countTokens();
		// At least 5 token
		if (numTokens != 5) {
			throw new RuntimeException("[" + entry
					+ "] invalid format [min hour dayOfMonth month dayOfWeek]");
		}
		for (int i = 0; tokenizer.hasMoreElements(); i++) {
			String token = tokenizer.nextToken();
			if (token.equals("*"))
				continue;
			switch (i) {
			case 0: // Minutes
				boolean[] bMinutes = new boolean[60];
				parseToken(token, bMinutes, false);
				ceb.setMinutes(bMinutes);
				break;
			case 1: // Hours
				boolean[] bHours = new boolean[24];
				parseToken(token, bHours, false);
				ceb.setHours(bHours);
				break;
			case 2: // Days of month
				boolean[] bDaysOfMonth = new boolean[31];
				parseToken(token, bDaysOfMonth, true);
				ceb.setDaysOfMonth(bDaysOfMonth);
				break;
			case 3: // Months
				boolean[] bMonths = new boolean[12];
				parseToken(token, bMonths, true);
				ceb.setMonths(bMonths);
				break;
			case 4: // Days of week
				boolean[] bDaysOfWeek = new boolean[7];
				parseToken(token, bDaysOfWeek, false);
				ceb.setDaysOfWeek(bDaysOfWeek);
				break;
			default:
				break;
			}
		}

		return ceb;
	}

	/**
	 * return: min hour dayOfMonth month dayOfWeek
	 * 
	 * @param ceb
	 * @return String
	 * @throws CrontabEntryException
	 */
	public static String unmarshall(CrontabSchedule ceb) {
		return ceb.getEntry();

	}

	/**
	 * Parses a token and fills the array of booleans that represents this
	 * CrontabEntryBean
	 * 
	 * @param token
	 *            String to parser usually smth like [ * , 2-3 , 2,3,4 ,4/5 ]
	 * @param arrayBool
	 *            this array is the most efficient way to compare entries
	 * @param bBeginInOne
	 *            says if the array begins in 0 or in 1
	 * @throws CrontabEntryException
	 *             Error parsing the string
	 */

	private static void parseToken(String token, boolean[] arrayBool,
			boolean bBeginInOne) {
		// This line initializes all the array of booleans instead of doing so
		// in the CrontabEntryBean Constructor.
		// for (int i = 0; i < arrayBool.length ; i++) arrayBool[i]=false;

		int i;
		int index;
		int each = 1;
		try {

			// Look for step first
			index = token.indexOf("/");
			if (index > 0) {
				each = Integer.parseInt(token.substring(index + 1));
				if (each == 0) {
					throw new RuntimeException(
							"Never use expressions like */0 ");
				}
				token = token.substring(0, index);
			}

			if (token.equals("*")) {
				for (i = 0; i < arrayBool.length; i += each) {
					arrayBool[i] = true;
				}
				return;
			}

			index = token.indexOf(",");
			if (index > 0) {
				StringTokenizer tokenizer = new StringTokenizer(token, ",");
				while (tokenizer.hasMoreElements()) {
					parseToken(tokenizer.nextToken(), arrayBool, bBeginInOne);
				}
				return;
			}

			index = token.indexOf("-");
			if (index > 0) {
				int start = Integer.parseInt(token.substring(0, index));
				int end = Integer.parseInt(token.substring(index + 1));

				if (bBeginInOne) {
					start--;
					end--;
				}
				for (int j = start; j <= end; j += each)
					arrayBool[j] = true;
				return;
			}

			int iValue = Integer.parseInt(token);
			if (bBeginInOne) {
				iValue--;
			}
			arrayBool[iValue] = true;
			return;
		} catch (Exception e) {
			throw new RuntimeException("Smth was wrong with " + token);
		}
	}
}
